package ru.maksb.exchange.service;

import static java.util.regex.Pattern.matches;

import java.util.concurrent.atomic.AtomicInteger;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ru.maksb.exchange.domain.OrderSide;
import ru.maksb.exchange.domain.OrderType;
import ru.maksb.exchange.domain.request.CancelOrderRequest;
import ru.maksb.exchange.domain.request.LoginRequest;
import ru.maksb.exchange.domain.request.NewOrderRequest;
import ru.maksb.exchange.domain.request.Request;
import ru.maksb.exchange.domain.request.RequestType;
import ru.maksb.exchange.exceptions.MessageParseException;

public class MessageParser {
    private static final Logger logger = LoggerFactory.getLogger(MessageParser.class);
    private static final String LOGIN_REQUEST_REGEXP = "sequenceId=\\d*;login=\\w*";
    
    private static final String ORDER_CANCEL_REGEXP = "sequenceId=\\d*;referenceId=\\w*;msgType=cancelOrder";
    private static final int TARGET_REFERENCE_ID_ENTRY_INDEX = 1;
    
    private static final String HEARTBEAT_REGEXP = "sequenceId=\\d*;a";
    
    private static final String ORDER_NEW_REGEXP = "sequenceId=\\d*;referenceId=\\d*;msgType=newOrder;ticket=\\w*;type=[LM];side=[SB];amount=\\d*;price=\\d*(.\\d*)?";
                                                  //sequenceId=1;referenceId=0;msgType=newOrder;ticket=USD;type=L;side=S;amount=12;price=123
    private static final int SEQUENCE_ID_ENTRY_INDEX = 0;
    private static final int REFERENCE_ID_ENTRY_INDEX = 1;
    private static final int TICKET_ENTRY_INDEX = 3;
    private static final int TYPE_ENTRY_INDEX = 4;
    private static final int SIDE_ENTRY_INDEX = 5;
    private static final int AMOUNT_ENTRY_INDEX = 6;
    private static final int PRICE_ENTRY_INDEX = 7;
    private static final String KEY_VALUE_DELIMITER = "=";
    private static final String KEY_VALUE_PAIRS_DELIMITER = ";";
    
    private static AtomicInteger requestsIdentity = new AtomicInteger();
    
    public static LoginRequest parseLoginRequest(String message) throws MessageParseException {
        validateRegExp(LOGIN_REQUEST_REGEXP, message);
        return new LoginRequest(getValue(message.split(KEY_VALUE_PAIRS_DELIMITER)[1]));
    }
    
    private static String getValue(String keyvalue) {
        return keyvalue.split(KEY_VALUE_DELIMITER)[1];
    }
    
    private static void validateRegExp(String regExp, String message) throws MessageParseException {
        if (!matches(regExp, message)) {
            throw new MessageParseException();
        }
    }

    public static Request parseRequest(String message) throws MessageParseException {
        if (matches(HEARTBEAT_REGEXP, message)) {
            Request heartbeat = parseHeartBeat(message);
            return heartbeat;
        }
        if (matches(ORDER_NEW_REGEXP, message)) {
            NewOrderRequest request = parseNewOrderRequest(message);
            return request;
        }
        if (matches(ORDER_CANCEL_REGEXP, message)) {
            CancelOrderRequest request = parseCancelOrderRequest(message);
            return request;
        }
        throw new MessageParseException("Cannot parse message: " + message);
    }
    
    private static Request parseHeartBeat(String message) {
        Request heartbeat = new Request();
        String[] keyvalues = message.split(KEY_VALUE_PAIRS_DELIMITER);
        heartbeat.setSequenceId(getValue(keyvalues[SEQUENCE_ID_ENTRY_INDEX]));
        heartbeat.setRequestType(RequestType.HEARTBEAT);
        return heartbeat;
    }

    private static CancelOrderRequest parseCancelOrderRequest(String message) {
        CancelOrderRequest request = new CancelOrderRequest();
        String[] keyvalues = message.split(KEY_VALUE_PAIRS_DELIMITER);
        request.setRequestType(RequestType.ORDER_CANCEL);
        request.setSequenceId(getValue(keyvalues[SEQUENCE_ID_ENTRY_INDEX]));
        request.setReferenceId(String.valueOf(requestsIdentity.getAndIncrement()));
        request.setTargetOrderReference(getValue(keyvalues[TARGET_REFERENCE_ID_ENTRY_INDEX]));
        return request;
    }

    private static NewOrderRequest parseNewOrderRequest(String message) throws MessageParseException {
        String[] keyValues = message.split(KEY_VALUE_PAIRS_DELIMITER);
        try {
            NewOrderRequest request = new NewOrderRequest();
            request.setTicket(getValue(keyValues[TICKET_ENTRY_INDEX]));
            request.setAmount(Integer.parseInt(getValue(keyValues[AMOUNT_ENTRY_INDEX])));
            request.setPrice(Double.parseDouble(getValue(keyValues[PRICE_ENTRY_INDEX])));
            request.setRequestType(RequestType.ORDER_NEW);
            request.setSequenceId(getValue(keyValues[SEQUENCE_ID_ENTRY_INDEX]));
            request.setClientReferenceId(getValue(keyValues[REFERENCE_ID_ENTRY_INDEX]));
            request.setReferenceId(String.valueOf(requestsIdentity.getAndIncrement()));
            request.setSide(parseOrderSide(getValue(keyValues[SIDE_ENTRY_INDEX])));
            request.setType(parseOrderType(getValue(keyValues[TYPE_ENTRY_INDEX])));
            return request; 
        } catch (Throwable e) {
            throw new MessageParseException(e);
        }
    }
    
    public static void main(String[] args) {
        System.out.println(OrderType.LIMIT);
    }
    
    private static OrderSide parseOrderSide(String str) throws MessageParseException {
        if (OrderSide.BUY.getCode().equals(str)) {
            return OrderSide.BUY;
        } else if (OrderSide.SELL.getCode().equals(str)){
            return OrderSide.SELL;
        } else {
            throw new MessageParseException("cannot parse OrderSide");
        }
    }
    
    private static OrderType parseOrderType(String str) throws MessageParseException {
        if (OrderType.LIMIT.getCode().equals(str)) {
            return OrderType.LIMIT;
        } else if (OrderType.MARKET.getCode().equals(str)){
            return OrderType.MARKET;
        } else {
            throw new MessageParseException("cannot parse OrderType");
        }
        
    }
}
